#region imported Namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;

using BO = PHSRAG.Insight21.BusinessObjects;
using DataAccess = PHSRAG.Utility.DataAccess;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using SalaryDistribConstants = PHSRAG.Insight21.Personnel.Constants.SalaryDistributionsConstants;
using SplitConstants = PHSRAG.Insight21.Personnel.Constants.DistributionSplitsConstants;
using ArgumentIndex = PHSRAG.Insight21.Personnel.PersonnelDetailsArgumentIndex;
#endregion

namespace PHSRAG.Insight21.Personnel
{
	#region SalaryDistributions class
	/// <summary>
	/// The SalaryDistributions class supports the loading of salary distribution
	/// information for a given job map id and fiscal year id.
	/// This class acts as a container for objects of type SalaryDistribution.
	/// It will also load a sorted list with the split id as the key and 
	/// the associated the salary distribution object.
	/// It provides indexing support to access the contained distribution period objects
	/// </summary>
	public class SalaryDistributions : BO.BusinessObject, IEnumerable
	{
		#region Instance Variables
		private ArrayList salaryDistribs;
		private SortedList splitDistribRelation;
		#endregion

		#region Public properties
		/// <summary>
		/// Get the count of SalaryDistribution objects contained
		/// </summary>
		public int Count
		{
			get{return salaryDistribs.Count;}
		}
		#endregion

		#region Indexers
		/// <summary>
		/// Retrieve a SalaryDistribution from the SalaryDistributions instance, given 
		/// its ordinal position within the collection. If the ordinal position is 
		/// out of region, a null cast exception is thrown 
		/// </summary>
		public SalaryDistribution this[int index]
		{
			get{return (SalaryDistribution)salaryDistribs[index];}
		}
		#endregion

		#region Class methods
		/// <summary>
		/// Converts the given salarydistribution type to another (Internal to External or vice versa) 
		/// </summary>
		/// <param name="salaryDistrib">The salary distribution object to be converted</param>
		/// <returns>The converted salary distribution object</returns>
		public static SalaryDistribution ConvertDistributionType(SalaryDistribution salaryDistrib)
		{
			DataRow distribRow = salaryDistrib.GetDataRow();
			if (salaryDistrib is InternalSalaryDistribution)
			{
				distribRow[SalaryDistribConstants.IsExternalFundColumnName] = true;
				return new ExternalSalaryDistribution(distribRow);
			}
			else
			{
				distribRow[SalaryDistribConstants.IsExternalFundColumnName] = false;
				return new InternalSalaryDistribution(distribRow);
			}
		}
		#endregion

		#region Constructor
		/// <summary>
		/// Intializes the base class with the cache policy and set the DataContainer of the present instance
		/// </summary>
		/// <param name="dataContainer">Dataset passed from the calling class</param>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		public SalaryDistributions(DataSet dataContainer,ICachePolicy cachePolicy) : base(cachePolicy)
		{
			DataContainer = dataContainer;
		}
		#endregion

		#region Public instance methods
		/// <summary>
		/// Creates a new salarydistribution object after adding a salarydistribution row to the appropriate
		/// table in the datacontainer and adding a salarydistribution object to the contained arraylist
		/// </summary>
		/// <param name="distributionType">SalaryDistributionType enumeration (Internal/External)</param>
		/// <returns>An empty salary distribution object of the type requested</returns>
		public SalaryDistribution CreateNewSalaryDistribution(SalaryDistributionType distributionType)
		{
			DataRow distribRow = 
				DataContainer.Tables[SalaryDistribConstants.SalaryDistributionsTableName].NewRow();
			DataContainer.Tables[SalaryDistribConstants.SalaryDistributionsTableName].Rows.Add(distribRow);
			
			switch (distributionType)
			{
				case SalaryDistributionType.Internal:
					distribRow[SalaryDistribConstants.IsExternalFundColumnName] = false;
					return new InternalSalaryDistribution(distribRow);
				case SalaryDistributionType.External:
					distribRow[SalaryDistribConstants.IsExternalFundColumnName] = true;
					return new ExternalSalaryDistribution(distribRow);
				default:
					throw new Exception(@"The distribution type given is invalid");
			}
		}
		/// <summary>
		/// Get the salary distribution to which a given split is associated to
		/// </summary>
		/// <param name="splitID">The ID of the split whose associated salary distribution has to be retrieved</param>
		/// <returns>Salary distribution object</returns>
		public SalaryDistribution GetSalaryDistribution(int splitID)
		{
			return splitDistribRelation.
				GetByIndex(splitDistribRelation.IndexOfKey(splitID)) as SalaryDistribution;
		}
		#endregion

		#region IEnumerable Members
		/// <summary>
		/// Get the enumerator associated with the instance
		/// </summary>
		/// <returns>Object of type IEnumerator</returns>
		public IEnumerator GetEnumerator()
		{
			return salaryDistribs.GetEnumerator();
		}

		#endregion
		
		#region BusinessObject methods
		/// <summary>
		/// This method is not used and only exists to satisfy interface requirements.
		/// </summary>
		public override void Cache()
		{
			throw new Exception("The salarydistributions object cannot be cached independently");
		}
		/// <summary>
		/// This method is not used and only exists to satisfy interface requirements.
		/// </summary>
		public override void UnCache()
		{
			throw new Exception("The salarydistributions object cannot be uncached independently");
		}
		/// <summary>
		/// Load the salary distribution data accessible to the currently logged in user and
		/// load the contained arraylist with as many SalaryDistribution objects as the number
		/// of rows in the salary  distribution table.
		/// Load the related distribution splits in relation with the SalaryDistributionIds.
		/// </summary>
		/// <param name="connectionString">The database connection string</param>
		/// <param name="args">Is expected to contain 3 mandatory arguments -
		/// DomainUserID,EmployeeJobMapID and FiscalYearID</param>
		public override void Load(string connectionString, params object[] args)
		{
			DataAccess.LoadDataSet
				(DataContainer
				,SalaryDistribConstants.SalaryDistributionsTableName
				,connectionString
				,SalaryDistribConstants.GetSalaryDistributionsSPROCName
				,Utility.DataAccess.BuildSqlParameterArray
				("@domainUserId",SqlDbType.Int,args[(int)ArgumentIndex.DomainUserID]
				,"@employeeJobMapId", SqlDbType.Int, args[(int)ArgumentIndex.EmployeeJobMapID]
				,"@fiscalYearId",SqlDbType.Int,args[(int)ArgumentIndex.FiscalYearID]));

			salaryDistribs = new ArrayList();
			foreach(DataRow row in 
				DataContainer.Tables[SalaryDistribConstants.SalaryDistributionsTableName].Rows)
			{
				salaryDistribs.Add(
					(Convert.ToBoolean(row[SalaryDistribConstants.IsExternalFundColumnName]))
					?(SalaryDistribution)new ExternalSalaryDistribution(row)
					:(SalaryDistribution)new InternalSalaryDistribution(row));
			}
			BuildSplitDistribRelation();
		}
		#endregion

		#region Private methods
		/// <summary>
		/// Builds the contained sorted list with the splitID as the key and the related salary distribution object
		/// </summary>
		private void BuildSplitDistribRelation()
		{
			splitDistribRelation = new SortedList();

			foreach(DataRow splitRow
						in DataContainer.Tables[SplitConstants.DistributionSplitsTableName].Rows)
			{				
	
				//Select the salary distribution row in the Distribution table with
				//the DistributionId equal to the SalaryDistribId in the split table
				DataRow[] salaryDistribRows = 
					DataContainer.Tables[SalaryDistribConstants.SalaryDistributionsTableName].Select
					(string.Format("{0}='{1}'"
					,SalaryDistribConstants.DistributionIDColumnName
					,splitRow[SplitConstants.SalaryDistributionIDColumnName]));

				//Since the select is on ID column we can directly index the Rows collection
				splitDistribRelation.Add
					(splitRow[SplitConstants.SplitIDColumnName]
					,(Convert.ToBoolean(salaryDistribRows[0][SalaryDistribConstants.IsExternalFundColumnName]))
					?(SalaryDistribution)new ExternalSalaryDistribution(salaryDistribRows[0])
					:(SalaryDistribution)new InternalSalaryDistribution(salaryDistribRows[0]));
			}
		}
		#endregion
	}
	#endregion
}